<?php
/**
 * Created by JetBrains PhpStorm.
 * User: Dmitry Khavilo (mailto: pquery@widowmaker.kiev.ua, blog: widowmaker.habrahabr.ru)
 * Date: 20.03.11
 * Time: 11:33
 */

  class pQuery implements ArrayAccess
  {
    const ERRORMODE_SUPRESS = 0;
    const ERRORMODE_ERROR = 1;
    const ERRORMODE_EXCEPTION = 2;

    public static $error_mode = self::ERRORMODE_ERROR;

    /** @var DOMElement[] */
    private $_selection = array();
    /** @var pQuery */
    private $_previous = null;
    /** @var pQuery */
    private $_root = null;
    /** @var DOMDocument */
    private $_document = null;

    /**
     * @param DOMDocument|DOMNodeList|DOMElement|string $nodes
     * @param null|pQuery $previous
     */
    public function __construct($nodes, pQuery $previous = null)
    {
      $this->_previous = $previous;
      if ($nodes instanceof DOMDocument)
        $this->importDocument($nodes);
      elseif ($nodes instanceof DOMNodeList)
        $this->importNodeList($nodes);
      elseif ($nodes instanceof DOMNode)
        $this->importNode($nodes);
      elseif (is_string($nodes))
        $this->importText($nodes);
      else
        $this->throwError('Invalid soucse data', E_USER_ERROR);
    }

    /**
     * @param  int $index
     * @return DOMElement
     */
    public function get($index = 0)
    {
      return $this->offsetGet($index);
    }

    /**\
     * @param  $name
     * @param pQuery|string $value
     * @return
     */
    public function attr($name, $value = null)
    {
      return is_null($value) ? $this->getAttr(&$name) : $this->setAttr(&$name, &$value);
    }

    /**
     * @return DOMDocument
     */
    public function getDocument()
    {
      if ($this->isRoot())
        return $this->_document;
      else
        return $this->getRoot()->getDocument();
    }

    /* Helper method */
    /**
     * @return pQuery
     */
    protected function getRoot()
    {
      if (!isset($this->_root))
        if (!is_null($this->_previous))
          $this->_root = $this->_previous->getRoot();
        else
          $this->_root = $this->_previous;

      return $this->_root;
    }

    /**
     * @return bool
     */
    protected function isRoot()
    {
      return (is_null($this->_previous));
    }

    /**
     * @param  string $name
     * @return null|string
     */
    protected function getAttr($name)
    {
      return ($el = $this->get()) && $el->hasAttribute($name) ? $el->getAttribute($name) : null;
    }

    /**
     * @param  string $name
     * @param  string $value
     * @return void
     */
    protected function setAttr($name, $value)
    {
      foreach($this->_selection as $node)
        $node->setAttribute($name, $value);
    }

    /**
     * @param DOMDocument $nodes
     * @return void
     */
    protected function importDocument(DOMDocument $nodes)
    {
      $this->_document = $nodes;
      // :TODO: implement
    }

    /**
     * @param DOMNodeList $nodes
     * @return void
     */
    protected function importNodeList(DOMNodeList $nodes)
    {
      // :TODO: implement
    }

    /**
     * @param DOMNode $nodes
     * @return void
     */
    protected function importNode(DOMNode $nodes)
    {
      // :TODO: implement
    }

    /**
     * @param string $nodes
     * @return void
     */
    protected function importText($nodes)
    {
      // :TODO: implement
    }

    /* Interface methods */
    /**
     * @param  int $offset
     * @return bool
     */
    public function offsetExists($offset)
    {
      return array_key_exists($offset, $this->_selection);
    }

    /**
     * @param  int $offset
     * @return DOMElement
     */
    public function offsetGet($offset)
    {
      if ($this->offsetExists($offset))
        return $this->_selection[$offset];

      $this->throwError('Undefined offset.');
    }

    /**
     * @param  int $offset
     * @param  mixed $value
     * @return void
     */
    public function offsetSet($offset, $value)
    {
      $this->throwError('Stupido errore. Use append(), prepend(), appendTo(), prepend(), xml() to insert nodes.', E_USER_ERROR);
    }

    /**
     * @param  int $offset
     * @return void
     */
    public function offsetUnset($offset)
    {
      unset($this->_selection[$offset]);
      $this->_selection = array_values($this->_selection);
    }

    /* error handling */
    /**
     * @throws pQuery_Exception
     * @param  string $message
     * @param  string $type
     * @return void
     */
    protected function throwError($message, $type = E_USER_NOTICE)
    {
      if (self::$error_mode == self::ERRORMODE_ERROR)
        trigger_error($message, $type);
      elseif(self::$error_mode == self::ERRORMODE_EXCEPTION && $type == E_USER_ERROR) //Throwing errors only
        throw new pQuery_Exception($message, $type);
    }
  }

  class pQuery_Exception extends Exception
  {
  }